///////////////////////////////////////////////////////////////////////////////
//
// Licensed under the MIT License
//
// Copyright (c) 2010 Chris Colinsky
// All Rights Reserved
//
// version	0.5
// http://code.google.com/p/sugarcookie/
// http://code.google.com/p/sugarcookie/wiki/License
//
////////////////////////////////////////////////////////////////////////////////

package sugarcookie.components.videoPlayer {
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	import mx.logging.ILogger;
	import mx.logging.Log;
	
	import sugarcookie.components.videoPlayer.PlayList;
	import sugarcookie.media.IVideoSource;
	import sugarcookie.media.VideoSourceConstants;;
	import sugarcookie.notifications.Notification;
	import sugarcookie.utils.IObservable;
	import sugarcookie.utils.IObserver;
	import sugarcookie.utils.Observable;
	import sugarcookie.utils.StringUtil;

	public class VideoController implements IObserver, IObservable {
		public static const LOCK_CONTROLS:String = "LOCK_CONTROLS";
		public static const PLAY:String = "PLAY";
		public static const PAUSE:String = "PAUSE";
		public static const SEEK:String = "SEEK";
		public static const BUFFERING:String = "BUFFERING";
		public static const SCRUB:String = "SCRUB";
		public static const PLAYLIST_UPDATE:String = "PLAYLIST_UPDATE";
		public static const PLAYLIST_COMPLETE:String = "PLAYLIST_COMPLETE";
		public static const RESET:String = "RESET";
		
		private var _log:ILogger;
		private var _video:IVideoSource;
		private var _seekBar:BasicSeekBar;
		private var _timecode:BasicTimecode;
		private var _playList:PlayList;
		private var _notifier:Observable;
		
		private var _scrubbing:Boolean = false;
		private var _lastSeek:Number;
		private var _perc:Number;
		private var _timer:Timer;
		
		public function VideoController() {
			_log = Log.getLogger(StringUtil.getFormattedClassName(this));
			_notifier = new Observable(this);
		}
		
		//Observable methods
		final public function addObserver(observer:IObserver,notificationType:String="all"):Boolean {
			return _notifier.addObserver(observer,notificationType);
		}
		
		final public function removeObserver(observer:IObserver,notificationType:String="all"):Boolean {
			return _notifier.removeObserver(observer,notificationType);
		}
		
		final public function clearObservers():void {
			_notifier.clearObservers();
		}
		
		final public function countObservers():Number {
			return _notifier.countObservers();
		}
		
		final public function get useLogging():Boolean { return _notifier.useLogging; }
		final public function set useLogging(value:Boolean):void { _notifier.useLogging = value; }
		
		final public function update(notification:Notification):void {
			switch (notification.type) {
				case VideoSourceConstants.LOAD_PROGRESS:
					if (_seekBar) _seekBar.updateLoadProgress(notification.data.percent);
					break;
				case VideoSourceConstants.STATUS_CHANGE:
					switch (notification.data.code) {
						case "NetStream.Seek.Notify":
							if (!_scrubbing) return;
							_video.resume();
							_timer.start();
							break;
					}
					break;
				case VideoSourceConstants.PLAYBACK_COMPLETE:
					_timer.reset();
					checkPlayListStatus();
					break;
				case VideoSourceConstants.META_DATA:
					notifyObservers(PLAYLIST_UPDATE,_playList.current);
					break;
				case VideoSourceConstants.BUFFERING:
					notifyObservers(LOCK_CONTROLS,{status:notification.data.status});
					notifyObservers(BUFFERING, {percent:notification.data.percent, status:notification.data.status });
					break;
				case SCRUB:
					_scrubbing = true;
					_video.pause();
					_timer.reset();
					break;
				case SEEK:
					_lastSeek = notification.data.percent;
					_video.seek(int(_video.videoDur*_lastSeek));
					break;
			}
		}
		
		final public function init(video:IVideoSource,seekBar:BasicSeekBar=null,timecode:BasicTimecode=null):void {
			_timer = new Timer(10);
			_timer.addEventListener(TimerEvent.TIMER,draw);
			
			_video = video;
			_video.addObserver(this);
			
			_seekBar = seekBar;
			if (_seekBar) _seekBar.addObserver(this);
			
			_timecode = timecode;
		}
		
		final public function start_playback(playList:PlayList):void {
			_playList = playList;
			_video.source = _playList.current.uri;
			//_video.volume = _playList.current.volume;
			_video.play();
			_timer.start();
		}
		
		final public function playPauseToggle():void {
			if (_video.isPlaying && !_video.isPaused) {
				_video.pause();
				_timer.reset();
			} else if (_video.isPaused && !_video.isPlaying) {
				_video.resume();
				_timer.start();
			} else if (!_video.isPaused && !_video.isPlaying) {
				_video.play();
				_timer.start();
			}
		}
		
		public function volume(vol:Number):void {
			_video.volume = vol;
		}
		
		public function pause():void {
			_video.pause();
			_timer.reset();
		}
		
		public function resume():void {
			_video.resume();
			_timer.start();
		}
		
		public function reset():void {
			_timer.reset();
			_video.close();
			
			//TODO - change to notify
			if (_seekBar) { 
				_seekBar.updatePlayProgress(0,.15,true);
				_seekBar.updateLoadProgress(0,.15,true);
			}
			if (_timecode) _timecode.updateTimeCode(0);
			notifyObservers(RESET);
		}
		
		private function notifyObservers(type:String,data:Object=null):void {
			_notifier.setChanged();
			_notifier.notifyObservers(type,data);
		}
		
		private function draw(evt:TimerEvent):void {
			var newPerc:Number = int(Math.ceil(_video.currTime)) / int(_video.videoDur);
			if ((newPerc==_perc && _scrubbing) || isNaN(newPerc)) return;
			else _scrubbing = false;
			
			//TODO - change to notify
			if (_seekBar) _seekBar.updatePlayProgress(newPerc,newPerc<_perc?.2:1);
			if (_timecode) _timecode.updateTimeCode(_video.currTime,_video.videoDur);
			
			_perc = newPerc;
			evt.updateAfterEvent();
		}
		
		private function checkPlayListStatus():void {
			if (!_playList.next()) {
				_log.warn("End of PlayList reached, dispatching close notification.");
				notifyObservers(PLAYLIST_COMPLETE);
				reset();
			}
		}
	}
}